from fastapi import APIRouter, HTTPException, Query, Request, Depends, Path as FastAPIPath, Form
from fastapi.responses import JSONResponse, RedirectResponse
from pathlib import Path
from typing import List, Optional, Any
import logging
import traceback
import uuid
from datetime import datetime

# 从main.py导入templates对象
from app.template_config import templates

# 导入Pydantic模型
from pydantic import BaseModel, Field, field_validator, ValidationError



# 导入服务层
try:
    from app.service.course_service import course_service
    from app.model.course_model import Course
    
except ImportError as e:
    print(f"导入错误: {e}")
    # 确保即使导入失败也不会导致模块无法加载
    course_service = None
    Course = None

# 配置日志 - 更高级的配置
logger = logging.getLogger(__name__)

# 如果logger没有处理器，则添加一个
if not logger.handlers:
    # 创建控制台处理器
    console_handler = logging.StreamHandler()
    console_handler.setLevel(logging.INFO)
    
    # 创建格式化器 - 包含更多上下文信息
    formatter = logging.Formatter(
        '%(asctime)s - %(name)s - %(levelname)s - '  
        'RequestID: %(request_id)s - %(message)s',
        datefmt='%Y-%m-%d %H:%M:%S'
    )
    console_handler.setFormatter(formatter)
    
    # 添加处理器到logger
    logger.addHandler(console_handler)
    logger.setLevel(logging.INFO)


# 创建APIRouter实例
api_router = APIRouter(
    prefix="/api/courses",
    tags=["courses"],
    responses={404: {"description": "Not found"}},
)

# 创建HTML路由实例
router = APIRouter()

# 依赖项：生成请求ID
def get_request_id(request: Request) -> str:
    """生成或获取请求ID"""
    request_id = request.headers.get("X-Request-ID")
    if not request_id:
        request_id = str(uuid.uuid4())
    return request_id


# 自定义日志记录装饰器
def log_operation(operation_name: str):
    """用于记录操作的装饰器"""
    def decorator(func):
        def wrapper(*args, **kwargs):
            # 获取请求和请求ID（从kwargs中）
            request = kwargs.get('request', None)
            request_id = kwargs.get('request_id', 'unknown')
            
            # 添加请求ID到日志记录的extra中
            extra = {'request_id': request_id}
            
            # 记录操作开始
            logger.info(f"开始 {operation_name} 操作", extra=extra)
            
            start_time = datetime.now()
            try:
                # 执行原始函数
                result = func(*args, **kwargs)
                
                # 计算执行时间
                execution_time = (datetime.now() - start_time).total_seconds()
                logger.info(
                    f"{operation_name} 操作成功完成，耗时: {execution_time:.3f}s",
                    extra=extra
                )
                return result
            except Exception as e:
                # 记录操作失败
                execution_time = (datetime.now() - start_time).total_seconds()
                logger.error(
                    f"{operation_name} 操作失败，耗时: {execution_time:.3f}s, 错误: {str(e)}",
                    extra=extra
                )
                raise
        return wrapper
    return decorator


# 自定义错误响应模型
class ErrorResponse(BaseModel):
    error_code: int
    message: str
    detail: Optional[str] = None


# 检查服务是否可用的依赖项
def check_service_availability():
    """检查course_service是否可用"""
    if course_service is None:
        raise HTTPException(
            status_code=503,
            detail="服务暂时不可用，请稍后再试"
        )
    return course_service


# 全局异常处理器
# 注意：异常处理器应该添加到FastAPI主应用实例中，而不是APIRouter
# 此处移除了@router.exception_handler装饰器相关的代码
# 这些异常处理器应该在主应用中实现


# 请求模型 - 创建图书
class CourseCreate(BaseModel):
    name: str = Field(..., min_length=1, max_length=100, description="图书名称")
    credit: float = Field(..., ge=0.5, le=10.0, description="学分")
    
    @field_validator('credit')
    def validate_credit(cls, v):
        if v <= 0:
            raise ValueError('学分必须大于0')
        return v


# 请求模型 - 更新图书
class CourseUpdate(BaseModel):
    name: Optional[str] = Field(None, min_length=1, max_length=100, description="图书名称")
    credit: Optional[float] = Field(None, ge=0.5, le=10.0, description="学分")
    
    @field_validator('credit')
    def validate_credit(cls, v):
        if v is not None and v <= 0:
            raise ValueError('学分必须大于0')
        return v


# 响应模型 - 图书信息
class CourseResponse(BaseModel):
    id: int
    name: str
    credit: float
    
    class Config:
        from_attributes = True
        
    @classmethod
    def from_course(cls, course: Course) -> 'CourseResponse':
        """从Course模型转换为响应模型"""
        return cls(
            id=course.id,
            name=course.name,
            credit=course.credit
        )


# 创建图书
@api_router.post("/", response_model=CourseResponse, status_code=201)
def create_course_api(
    request: Request,
    course_data: CourseCreate,
    request_id: str = Depends(get_request_id),
    service: Any = Depends(check_service_availability)
):
    """创建新图书"""
    extra = {'request_id': request_id}
    logger.info(f"收到创建图书请求: 图书名={course_data.name}", extra=extra)
    
    try:
        # 额外的业务逻辑验证
        if len(course_data.name.strip()) == 0:
            raise HTTPException(status_code=400, detail="图书名称不能为空字符串")
            
        # 调用服务层创建图书
        new_course = service.create_course(
            name=course_data.name,
            credit=course_data.credit
        )
        logger.info(f"成功创建图书: ID={new_course.id}, 名称={new_course.name}", extra=extra)
        return CourseResponse.from_course(new_course)
    except HTTPException:
        raise
    except ValueError as e:
        logger.warning(f"创建图书失败: {str(e)}", extra=extra)
        raise HTTPException(status_code=400, detail=str(e))
    except Exception as e:
        logger.error(f"创建图书时发生错误: {str(e)}", extra=extra)
        logger.error(traceback.format_exc())
        raise HTTPException(status_code=500, detail="创建图书时发生内部错误")


# 获取所有图书
@api_router.get("/", response_model=List[CourseResponse])
def get_all_courses_api(
    request: Request,
    request_id: str = Depends(get_request_id),
    service: Any = Depends(check_service_availability)
):
    """获取所有图书列表"""
    extra = {'request_id': request_id}
    logger.info("收到获取所有图书请求", extra=extra)
    
    try:
        courses = service.get_all_courses()
        logger.info(f"获取图书列表成功，共 {len(courses)} 个图书", extra=extra)
        return [CourseResponse.from_course(course) for course in courses]
    except Exception as e:
        logger.error(f"获取图书列表时发生错误: {str(e)}", extra=extra)
        logger.error(traceback.format_exc())
        raise HTTPException(status_code=500, detail="获取图书列表时发生内部错误")


# 根据ID获取图书
@api_router.get("/{course_id}", response_model=CourseResponse)
def get_course_by_id_api(
    request: Request,
    course_id: int = FastAPIPath(..., ge=1, description="图书ID"),
    request_id: str = Depends(get_request_id),
    service: Any = Depends(check_service_availability)
):
    """根据ID获取图书详情"""
    extra = {'request_id': request_id}
    logger.info(f"收到获取图书详情请求: 图书ID={course_id}", extra=extra)
    
    try:
        # 验证course_id参数
        if course_id <= 0:
            raise HTTPException(status_code=400, detail="图书ID必须为正整数")
            
        course = service.get_course_by_id(course_id)
        logger.info(f"获取图书ID {course_id} 成功: 名称={course.name}", extra=extra)
        return CourseResponse.from_course(course)
    except HTTPException:
        raise
    except ValueError as e:
        logger.warning(f"图书ID {course_id} 不存在: {str(e)}", extra=extra)
        raise HTTPException(status_code=404, detail=str(e))
    except Exception as e:
        logger.error(f"获取图书ID {course_id} 时发生错误: {str(e)}", extra=extra)
        logger.error(traceback.format_exc())
        raise HTTPException(status_code=500, detail="获取图书详情时发生内部错误")


# 更新图书
@api_router.put("/{course_id}", response_model=CourseResponse)
def update_course_api(
    request: Request,
    course_id: int = FastAPIPath(..., ge=1, description="图书ID"),
    course_data: CourseUpdate = ...,
    request_id: str = Depends(get_request_id),
    service: Any = Depends(check_service_availability)
):
    """更新图书信息"""
    extra = {'request_id': request_id}
    logger.info(f"收到更新图书请求: 图书ID={course_id}, 更新数据={course_data.model_dump(exclude_unset=True)}", extra=extra)
    
    try:
        # 验证course_id参数
        if course_id <= 0:
            raise HTTPException(status_code=400, detail="图书ID必须为正整数")
            
        # 将Pydantic模型转换为字典并过滤掉None值
        update_data = course_data.model_dump(exclude_unset=True)
        
        # 如果没有提供更新数据
        if not update_data:
            raise HTTPException(status_code=400, detail="至少需要提供一个更新字段")
        
        # 额外的业务逻辑验证
        if 'name' in update_data and len(update_data['name'].strip()) == 0:
            raise HTTPException(status_code=400, detail="图书名称不能为空字符串")
        
        updated_course = service.update_course(
            course_id,
            **update_data
        )
        logger.info(f"更新图书ID {course_id} 成功: 新名称={updated_course.name}", extra=extra)
        return CourseResponse.from_course(updated_course)
    except HTTPException:
        raise
    except ValueError as e:
        logger.warning(f"更新图书ID {course_id} 失败: {str(e)}", extra=extra)
        raise HTTPException(status_code=400, detail=str(e))
    except Exception as e:
        logger.error(f"更新图书ID {course_id} 时发生错误: {str(e)}", extra=extra)
        logger.error(traceback.format_exc())
        raise HTTPException(status_code=500, detail="更新图书时发生内部错误")


# 删除图书
@api_router.delete("/{course_id}", status_code=204)
def delete_course_api(
    request: Request,
    course_id: int = FastAPIPath(..., ge=1, description="图书ID"),
    request_id: str = Depends(get_request_id),
    service: Any = Depends(check_service_availability)
):
    """删除图书"""
    extra = {'request_id': request_id}
    logger.info(f"收到删除图书请求: 图书ID={course_id}", extra=extra)
    
    try:
        # 验证course_id参数
        if course_id <= 0:
            raise HTTPException(status_code=400, detail="图书ID必须为正整数")
            
        service.delete_course(course_id)
        logger.info(f"删除图书ID {course_id} 成功", extra=extra)
        return None
    except HTTPException:
        raise
    except ValueError as e:
        logger.warning(f"删除图书ID {course_id} 失败: {str(e)}", extra=extra)
        raise HTTPException(status_code=404, detail=str(e))
    except Exception as e:
        logger.error(f"删除图书ID {course_id} 时发生错误: {str(e)}", extra=extra)
        logger.error(traceback.format_exc())
        raise HTTPException(status_code=500, detail="删除图书时发生内部错误")


# 根据名称搜索图书
@api_router.get("/search/name", response_model=List[CourseResponse])
def search_courses_by_name_api(
    request: Request,
    keyword: str = Query(..., min_length=1, max_length=100, description="搜索关键字"),
    request_id: str = Depends(get_request_id),
    service: Any = Depends(check_service_availability)
):
    """根据图书名称搜索图书"""
    extra = {'request_id': request_id}
    logger.info(f"收到搜索图书请求: 关键字='{keyword}'", extra=extra)
    
    try:
        # 额外的参数验证
        if not keyword or keyword.isspace():
            raise HTTPException(status_code=400, detail="搜索关键字不能为空或只包含空格")
            
        courses = service.search_courses_by_name(keyword)
        logger.info(f"搜索图书关键字 '{keyword}' 成功，共找到 {len(courses)} 个图书", extra=extra)
        return [CourseResponse.from_course(course) for course in courses]
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"搜索图书时发生错误: {str(e)}", extra=extra)
        logger.error(traceback.format_exc())
        raise HTTPException(status_code=500, detail="搜索图书时发生内部错误")


# 根据学分范围查询图书
@api_router.get("/search/credit", response_model=List[CourseResponse])
def get_courses_by_credit_range_api(
    request: Request,
    min_credit: float = Query(..., ge=0.5, le=10.0, description="最小学分"),
    max_credit: float = Query(..., ge=0.5, le=10.0, description="最大学分"),
    request_id: str = Depends(get_request_id),
    service: Any = Depends(check_service_availability)
):
    """根据学分范围查询图书"""
    extra = {'request_id': request_id}
    logger.info(f"收到按学分范围查询图书请求: 范围={min_credit}-{max_credit}", extra=extra)
    
    try:
        # 验证学分范围
        if min_credit > max_credit:
            raise HTTPException(status_code=400, detail="最小学分不能大于最大学分")
            
        if min_credit < 0 or max_credit < 0:
            raise HTTPException(status_code=400, detail="学分必须为正数")
        
        courses = service.get_courses_by_credit_range(min_credit, max_credit)
        logger.info(f"查询学分范围 {min_credit}-{max_credit} 的图书成功，共找到 {len(courses)} 个图书", extra=extra)
        return [CourseResponse.from_course(course) for course in courses]
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"按学分范围查询图书时发生错误: {str(e)}", extra=extra)
        logger.error(traceback.format_exc())
        raise HTTPException(status_code=500, detail="按学分范围查询图书时发生内部错误")


# HTML路由函数
@router.get("/courses")
def get_courses_html(request: Request):
    """显示所有图书的HTML页面"""
    try:
        courses = course_service.get_all_courses()
        return templates.TemplateResponse("courses.html", {"request": request, "courses": courses, "messages": []})
    except Exception as e:
        return templates.TemplateResponse("courses.html", {"request": request, "courses": [], "error": str(e), "messages": []})


@router.get("/courses/create")
def create_course_html(request: Request):
    """显示创建图书的表单页面"""
    return templates.TemplateResponse("create_course.html", {"request": request, "messages": []})


@router.get("/courses/{course_id}")
def get_course_html(request: Request, course_id: int = FastAPIPath(..., ge=1)):
    """显示单个图书详情的HTML页面"""
    try:
        course = course_service.get_course_by_id(course_id)
        return templates.TemplateResponse("course_detail.html", {"request": request, "course": course, "messages": []})
    except ValueError as e:
        return templates.TemplateResponse("course_detail.html", {"request": request, "course": None, "error": str(e), "messages": []})
    except Exception as e:
        return templates.TemplateResponse("course_detail.html", {"request": request, "course": None, "error": "获取图书详情失败", "messages": []})


@router.post("/courses/create")
def create_course_html(request: Request, name: str = Form(...), credit: float = Form(...)):
    """处理创建图书的表单提交"""
    try:
        # 直接传递参数给service层
        course_service.create_course(name=name, credit=credit)
        return RedirectResponse(url="/courses", status_code=302)
    except ValueError as e:
        return templates.TemplateResponse("create_course.html", {"request": request, "error": str(e), "name": name, "credit": credit, "messages": []})
    except Exception as e:
          return templates.TemplateResponse("create_course.html", {"request": request, "error": "创建图书失败", "name": name, "credit": credit, "messages": []})


@router.get("/courses/{course_id}/edit")
def edit_course_html(request: Request, course_id: int = FastAPIPath(..., ge=1)):
    """显示编辑图书的表单页面"""
    try:
        course = course_service.get_course_by_id(course_id)
        return templates.TemplateResponse("edit_course.html", {"request": request, "course": course, "messages": []})
    except ValueError as e:
        return templates.TemplateResponse("edit_course.html", {"request": request, "course": None, "error": str(e), "messages": []})
    except Exception as e:
        return templates.TemplateResponse("edit_course.html", {"request": request, "course": None, "error": "获取图书信息失败", "messages": []})


@router.post("/courses/{course_id}/edit")
def edit_course_html(request: Request, course_id: int = FastAPIPath(..., ge=1), name: str = Form(...), credit: float = Form(...)):
    """处理编辑图书的表单提交"""
    try:
        # 直接传递参数给service层
        course_service.update_course(course_id, name=name, credit=credit)
        return RedirectResponse(url=f"/courses/{course_id}", status_code=302)
    except ValueError as e:
        # 获取当前图书信息以显示在表单中
        try:
            current_course = course_service.get_course_by_id(course_id)
        except:
            current_course = None
        return templates.TemplateResponse("edit_course.html", {"request": request, "course": current_course, "error": str(e), "messages": []})
    except Exception as e:
        try:
            current_course = course_service.get_course_by_id(course_id)
        except:
            current_course = None
        return templates.TemplateResponse("edit_course.html", {"request": request, "course": current_course, "error": "更新图书失败", "messages": []})


@router.post("/courses/{course_id}/delete")
def delete_course_html(request: Request, course_id: int = FastAPIPath(..., ge=1)):
    """处理删除图书的请求"""
    try:
        course_service.delete_course(course_id)
        return RedirectResponse(url="/courses", status_code=303)
    except ValueError as e:
        # 删除失败后重定向回图书列表
        courses = course_service.get_all_courses() if course_service else []
        return templates.TemplateResponse("courses.html", {"request": request, "courses": courses, "error": str(e), "messages": []})
    except Exception as e:
        # 记录错误但仍重定向回图书列表
        courses = course_service.get_all_courses() if course_service else []
        return templates.TemplateResponse("courses.html", {"request": request, "courses": courses, "error": "删除图书失败", "messages": []})


# 将API路由添加到主路由
router.include_router(api_router)